package org.dea.facade;

import java.io.File;
import java.lang.reflect.Modifier;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.util.Iterator;
import java.util.Vector;

import org.dea.util.SourceUtil;

import de.gulden.util.javasource.Class;
import de.gulden.util.javasource.Code;
import de.gulden.util.javasource.Constructor;
import de.gulden.util.javasource.Exception;
import de.gulden.util.javasource.Method;
import de.gulden.util.javasource.NamedIterator;
import de.gulden.util.javasource.Parameter;
import de.gulden.util.javasource.Type;

public class SwitchFacadeMaker implements FacadeMaker {

	public static final String IF_PREFIX = RMIInterfaceFacadeMaker.IF_PREFIX;
	public static final String SWITCH_PREFFIX = "Switch";
	private final String mappingFile; 
	
	public SwitchFacadeMaker(String mappingFile ) {
		this.mappingFile = mappingFile;
	}
	
	public Class generateFacade(File sourceDir, String fullQualifiedName) throws java.lang.Exception{

		Class baseClass = SourceUtil.loadClassSource(sourceDir, fullQualifiedName);
		if( baseClass == null )
			throw new RuntimeException("Class "+fullQualifiedName+" does not exist on sourceDir "+ sourceDir);
		String interfaceName = IF_PREFIX+baseClass.getUnqualifiedName();
		SourceUtil.changeClassName(baseClass, SWITCH_PREFFIX+baseClass.getUnqualifiedName());
		SourceUtil.addExceptionAllMethods(baseClass, "RemoteException" );
		SourceUtil.addImport(baseClass, "java.rmi", "*");
		SourceUtil.addImport(baseClass, "java.rmi.registry", "*");
		SourceUtil.addImport(baseClass, "java.util", "*");
		SourceUtil.addImport(baseClass, "org.dea.mapping", "*");
		SourceUtil.addField(baseClass, "distributedComponents", "Map");
		modifyMethodsCode(baseClass);
		addSwitchIDParater(baseClass);
		createConstructors(baseClass);
		addGetFacade(baseClass, interfaceName);
		SourceUtil.generateSourceFile(sourceDir, baseClass);
		return baseClass;
	}
	
	private void createConstructors( Class baseClass ) {
		Vector cons = SourceUtil.getConstructors(baseClass);
		for (Iterator iter = cons.iterator(); iter.hasNext();) {
			Constructor constructor = (Constructor) iter.next();
//			System.out.println(constructor.myParameter+ " "+ constructor);
			Code c = new Code();
			c.setRaw(constructor.getCode().getRaw()+"\n\tdistributedComponentsList = EasyAcceptMappingReader.processMappingFile( \""+mappingFile+"\");\n");
			constructor.setCode(c);
			SourceUtil.addExcecption(constructor, "Exception");
		}
	}
	
	private void addSwitchIDParater( Class baseClass ) {

		String parameterName = "switchID";
		String parameterType = "String";
		Vector methods = SourceUtil.getMethods(baseClass);
		for ( int i = 0; i < methods.size(); i++ ) {
			Method method = (Method) methods.get( i );
			SourceUtil.addParameter(parameterName, parameterType, method);
		}
	}
	
	private void addGetFacade( Class baseFacadeClass, String facadeType ){
		String codeRaw = "";

		codeRaw += "DistributedComponent cpm = (DistributedComponent) distributedComponentsList.get( switchID );\n";
		codeRaw +="if( cpm == null ) 		throw new RuntimeException(\"Unmapped switchID: \"+switchID);\n";
		codeRaw += "String hostAddress = cpm.getHostAddress() + \":\" + cpm.getHostPort();\n";
        
		codeRaw += facadeType+" obj = null;\n";
		codeRaw += "try{\n";
		codeRaw += "Registry reg = LocateRegistry.getRegistry(cpm.getHostAddress(), cpm.getHostPort());\n";
		codeRaw += "\tobj = ("+facadeType+") reg.lookup(\""+facadeType+"\");\n";
		codeRaw += "} catch (Exception ex){\n";
		codeRaw += "\tex.printStackTrace();\n";
		codeRaw += "}\n";
		codeRaw += "\nreturn obj;\n";
		String[] paramNames ={ "switchID" };
		String[] paramTypes ={ "String" };
		SourceUtil.addMethod(baseFacadeClass, Modifier.PRIVATE, "getFacade", paramNames , paramTypes, facadeType, codeRaw );
	}
	
	/**
	 * 
	 */
	protected void modifyMethodsCode( Class baseFacadeClass ){

		Vector methods = SourceUtil.getMethods(baseFacadeClass);
		for ( int i = 0; i < methods.size(); i++ ) {
			Method method = (Method) methods.get( i );

			String invocationCode = method.getUnqualifiedName();
			invocationCode += getParamentersName( method.getParameters() );
			invocationCode = "return getFacade(switchID)" + "." + invocationCode + ";";

			Type type = method.getType();
			if ( type.getTypeName().equals( "void" ) )
				invocationCode = invocationCode.replace( "return ", "" );

			Code code = new Code();
			code.setRaw( invocationCode );
			method.setCode( code );

			NamedIterator exceptions = method.getExceptions();
			Exception exception = new Exception( method );
			exception.setName( "RemoteException" );
			exceptions.add( exception );
		}
	}
	
	/**
	 * @param parameters
	 */
	private String getParamentersName( NamedIterator parameters ) {

		String strParameters = "(";
		while ( parameters.hasMore() ) {
			strParameters += ((Parameter) parameters.next()).getName() + ",";
		}
		if ( strParameters.length() > 1 )
			strParameters = strParameters.substring( 0, strParameters.length() - 1 );
		strParameters += ")";

		return strParameters;

	}


	public static void main(String[] args) throws java.lang.Exception {
		new SwitchFacadeMaker("test/xml/onlyAlpha.xml").generateFacade(new File("test"), "somepack.innerpack.Simple");
	}

}
